from fastapi import FastAPI, UploadFile, File, HTTPException, Form
from fastapi.middleware.cors import CORSMiddleware
from moviepy.editor import VideoFileClip, vfx
import os
from tempfile import NamedTemporaryFile
from fastapi.responses import FileResponse
import logging
import shutil
from pathlib import Path
from PIL import Image
import imageio
from typing import Optional

app = FastAPI()

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# 配置CORS
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:5173"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 配置
MAX_FILE_SIZE = 50 * 1024 * 1024  # 50MB
MAX_GIF_SIZE = 300 * 1024  # 300KB (微信表情包大小限制)
MAX_WIDTH = 240  # 表情包最大宽度
TEMP_DIR = Path("temp")
TEMP_DIR.mkdir(exist_ok=True)


def optimize_gif(input_path: Path, output_path: Path, max_size: int = MAX_GIF_SIZE):
    """优化GIF文件大小"""
    with Image.open(input_path) as img:
        # 获取原始帧数据
        frames = []
        try:
            while True:
                frames.append(img.copy())
                img.seek(len(frames))
        except EOFError:
            pass

        # 计算适当的压缩参数
        quality = 256  # 始颜色数量
        while quality > 2:
            # 保存优化后的GIF
            frames[0].save(
                output_path,
                save_all=True,
                append_images=frames[1:],
                optimize=True,
                quality=10,  # 压缩质量
                colors=quality  # 颜色数量
            )
            
            # 检查文件大小
            if output_path.stat().st_size <= max_size:
                break
                
            # 如果文件仍然太大，减少颜色数量
            quality = quality // 2

        return output_path.stat().st_size


@app.post("/convert")
async def convert_video_to_gif(
        video: UploadFile = File(...),
        quality: str = "medium",
        start_time: float = Form(0.0),
        end_time: float = Form(-1.0),
):
    temp_video_path = None
    video_clip = None
    temp_gif_path = None
    final_gif_path = None
    
    try:
        # 检查文件大小
        content = await video.read()
        if len(content) > MAX_FILE_SIZE:
            raise HTTPException(status_code=400, detail="文件大小超过50MB限制")

        # 创建临时视频文件
        temp_video_path = TEMP_DIR / f"temp_video_{video.filename}"
        with open(temp_video_path, "wb") as f:
            f.write(content)

        # 创建临时GIF文件路径
        temp_gif_path = TEMP_DIR / f"temp_{video.filename}.gif"
        final_gif_path = TEMP_DIR / f"converted_{quality}_{video.filename}.gif"

        # 加载视频
        video_clip = VideoFileClip(str(temp_video_path))
        
        # 在视频处理前后添加日志
        logger.info(f"Original video duration: {video_clip.duration}s")
        logger.info(f"Requested time range: {start_time}s to {end_time}s")

        # 处理时间范围
        start_time = max(0, start_time)
        if end_time < 0:
            end_time = video_clip.duration
        else:
            end_time = min(end_time, video_clip.duration)
        
        # 只有当时间范围有效时才进行裁剪
        if 0 <= start_time < end_time <= video_clip.duration:
            logger.info(f"Trimming video from {start_time}s to {end_time}s")
            video_clip = video_clip.subclip(start_time, end_time)
        else:
            logger.warning(f"Invalid time range: {start_time}s to {end_time}s, using full video")

        # 裁剪后添加日志
        logger.info(f"Final clip duration: {video_clip.duration}s")

        # 质量设置
        quality_settings = {
            "high": {"fps": 12, "resize_factor": 0.8},
            "medium": {"fps": 10, "resize_factor": 0.6},
            "low": {"fps": 8, "resize_factor": 0.5}
        }

        settings = quality_settings.get(quality, quality_settings["medium"])

        # 调整视频大小和处理
        target_width = min(int(video_clip.w * settings["resize_factor"]), MAX_WIDTH)
        video_clip = (video_clip
                     .resize(width=target_width)  # 调整尺寸
                     .fx(vfx.colorx, 0.8)  # 稍微降低颜色饱和度
                     )

        # 转换为GIF
        video_clip.write_gif(
            str(temp_gif_path),
            fps=settings["fps"],
            program='ffmpeg'
        )

        # 进一步优化GIF文件大小
        gif_size = optimize_gif(temp_gif_path, final_gif_path)
        
        logger.info(f"GIF created successfully: {gif_size} bytes")

        # 关闭视频剪辑
        if video_clip:
            video_clip.close()

        # 返回文件响应
        return FileResponse(
            path=str(final_gif_path),
            media_type="image/gif",
            filename=f"sticker_{video.filename}.gif",
            background=None
        )

    except Exception as e:
        logger.error(f"转换失败: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))

    finally:
        # 清理资源
        if video_clip:
            try:
                video_clip.close()
            except:
                pass

        # 清理临时文件
        for path in [temp_video_path, temp_gif_path]:
            if path and path.exists():
                try:
                    path.unlink()
                except Exception as e:
                    logger.error(f"清理临时文件失败: {str(e)}")


@app.get("/")
async def read_root():
    return {"message": "视频转GIF服务已启动"}


@app.on_event("startup")
async def startup_event():
    TEMP_DIR.mkdir(exist_ok=True)


@app.on_event("shutdown")
async def shutdown_event():
    if TEMP_DIR.exists():
        try:
            shutil.rmtree(TEMP_DIR)
        except Exception as e:
            logger.error(f"清理临时目录失败: {str(e)}")
